package com.bosicloud.cmb.ibp.edge.controller;

import com.bosicloud.cmb.ibp.account.common.model.IbpUser;
import com.bosicloud.cmb.ibp.api.manager.api.ApiManagerClient;
import com.bosicloud.cmb.ibp.api.manager.commom.constant.SubscribeApiStatus;
import com.bosicloud.cmb.ibp.api.manager.commom.model.IbpApi;
import com.bosicloud.cmb.ibp.api.manager.commom.model.IbpUserConsumerApi;
import com.bosicloud.cmb.ibp.api.manager.commom.vo.*;
import com.bosicloud.cmb.ibp.commom.dto.UnionResult;
import com.bosicloud.cmb.ibp.commom.exception.ServiceException;
import com.bosicloud.cmb.ibp.edge.constant.SessionKeys;
import com.bosicloud.cmb.ibp.edge.constant.UrlDefine;
import com.bosicloud.cmb.ibp.edge.vo.AddApiParamVO;
import com.github.fge.jackson.JsonLoader;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.io.IOException;
import java.net.URL;
import java.util.Objects;
import java.util.Optional;

/**
 * @author Jack Peng / ziliang.peng@bosicloud.com
 */
@Api(value = "API管理接口", description = "管理API")
@RestController
@Slf4j
public class ApiManagerController {

    @Autowired
    ApiManagerClient apiManagerClient;


    @ApiOperation(value = "获取API分类信息", response = UnionResult.class)
    @GetMapping(value = UrlDefine.ALL_CATEGORY, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object allCategory(HttpServletRequest request) {

        return apiManagerClient.getAllCategory();
    }


    @HystrixCommand
    @ApiOperation(value = "分页获取API市场列表")
    @GetMapping(value = UrlDefine.SEARCH_API + "/{currentPage}/{pageSize}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchApiMarketList(@ApiParam(name = "currentPage", value = "当前页", required = true)
                                      @PathVariable(value = "currentPage") Integer currentPage,
                                      @ApiParam(name = "pageSize", value = "每页显示条数", required = true)
                                      @PathVariable(value = "pageSize") Integer pageSize,
                                      @ApiParam("分类ID") @RequestParam(required = false, value = "categoryId") Integer categoryId,
                                      @ApiParam("API名称") @RequestParam(required = false, value = "searchText") String searchText) {
        return apiManagerClient.searchApi(currentPage, pageSize, categoryId, searchText);
    }

    @ApiOperation(value = "API详情", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.API_DETAIL + "/{apiId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchApiDetail(@ApiParam(name = "apiId", value = "apiId", required = true)
                                  @PathVariable(value = "apiId") Integer apiId) {
        return apiManagerClient.searchApiDetail(apiId);
    }

    @ApiOperation(value = "根据apiId获取Api方法列表", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.METHOD_LIST + "/{apiId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchApiMethodListByApiId(@ApiParam(name = "apiId", value = "apiId", required = true)
                                             @PathVariable(value = "apiId") Integer apiId) {
        return apiManagerClient.searchApiMethodListByApiId(apiId);
    }

    @ApiOperation(value = "根据apiId查询方法详情", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.METHOD_INFO + "/{docsId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchApiMethodInfoByDocsId(@ApiParam(name = "docsId", value = "docsId", required = true)
                                              @PathVariable(value = "docsId") Integer docsId) {
        return apiManagerClient.searchApiMethodInfoByDocsId(docsId);
    }

    @ApiOperation(value = "添加API", response = UnionResult.class)
    @HystrixCommand
    @PostMapping(value = UrlDefine.ADD_API, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object addApi(@ApiParam(name = "paramVO", value = "ibpApiBean实体", required = true)
                         @RequestBody @Valid AddApiParamVO paramVO) {

        try {
            JsonLoader.fromURL(new URL(paramVO.getSwagger()));
        } catch (IOException e) {
            throw new ServiceException("JSON文件格式错误: " + e.getMessage(), UnionResult.StatusEnum.ParamsFail.code());
        }
        Optional.of(paramVO.getCreateUser())
                .filter(id -> !Objects.equals(id, 0))
                .orElseThrow(() -> new ServiceException("请正确填写createUser.",
                        UnionResult.StatusEnum.ParamsFail.code()));

        Optional.of(paramVO.getCategoryId())
                .filter(id -> !Objects.equals(id, 0))
                .orElseThrow(() -> new ServiceException("请正确填写categoryId.",
                        UnionResult.StatusEnum.ParamsFail.code()));

        Boolean checkApiName = apiManagerClient.checkApiName(paramVO.getApiName());
        if (checkApiName) {
            throw new ServiceException("API名称已存在.",
                    UnionResult.StatusEnum.ParamsFail.code());
        }

        IbpApi ibpApi = new IbpApi();
        BeanUtils.copyProperties(paramVO, ibpApi);
        return apiManagerClient.addApi(ibpApi);
    }

    @ApiOperation(value = "更新API的method 文档信息（参数描述，请求示例等）", response = UnionResult.class)
    @HystrixCommand
    @PutMapping(value = UrlDefine.METHOD_INFO, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object updateApiMethodInfo(@RequestBody MethodInfoVO methodInfoVO) {
        return apiManagerClient.updateApiMethodInfo(methodInfoVO);
    }

    @ApiOperation(value = "部署API", response = UnionResult.class)
    @HystrixCommand
    @PostMapping(value = UrlDefine.DEPLOY_API, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object deployApi(@ApiParam(name = "paramVO", value = "deployApiParamVO实体", required = true)
                            @RequestBody @Valid DeployApiParamVO paramVO) {

        int apiId = Integer.parseInt(paramVO.getApiId());
        IbpApiVO ibpApiVO = apiManagerClient.searchApiDetail(apiId);
        Optional.ofNullable(ibpApiVO)
                .orElseThrow(() -> new ServiceException("非法参数,apiId不存在.",
                        UnionResult.StatusEnum.ParamsFail.code()));

        Optional.of(ibpApiVO)
                .filter(rs -> !rs.getIsPublic())
                .orElseThrow(() -> new ServiceException("该API已经部署,请不要重复部署.",
                        UnionResult.StatusEnum.Fail.code()));

        apiManagerClient.searchUsagePlanList(apiId)
                .forEach(rs -> {
                    if (rs.getEnv().equalsIgnoreCase(paramVO.getStageName())) {
                        throw new ServiceException("该环境API已经部署,请不要重复部署.",
                                UnionResult.StatusEnum.Fail.code());
                    }
                });
        return apiManagerClient.deployApi(paramVO);
    }

    @HystrixCommand
    @ApiOperation(value = "Api开发者列表", response = UnionResult.class)
    @GetMapping(value = UrlDefine.API_LIST + "/{currentPage}/{pageSize}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchApiDeveloperList(@ApiParam(name = "currentPage", value = "当前页", required = true)
                                         @PathVariable(value = "currentPage") Integer currentPage,
                                         @ApiParam(name = "pageSize", value = "每页显示条数", required = true)
                                         @PathVariable(value = "pageSize") Integer pageSize,
                                         HttpServletRequest request) {
        return apiManagerClient.searchApiDeveloperList(currentPage, pageSize, getUserFromSession(request).getId());
    }

    private IbpUser getUserFromSession(HttpServletRequest request) {
        Object obj = request.getSession().getAttribute(SessionKeys.ACCOUNT);
        if (Objects.isNull(obj)) {
            throw new ServiceException("未找到会话信息.", UnionResult.StatusEnum.FailToken.code());
        }
        return (IbpUser) obj;

    }

    @ApiOperation(value = "订阅API", response = UnionResult.class)
    @HystrixCommand
    @PostMapping(value = UrlDefine.SUBSCRIBE_API, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object subscribeApi(
            @ApiParam(name = "paramVO", value = "订阅参数实体", required = true)
            @RequestBody @Valid SubscribeApiParamVO paramVO) {

        int apiId = Integer.parseInt(paramVO.getApiId());
        int userId = Integer.parseInt(paramVO.getUserId());
        IbpApiVO ibpApiVO = apiManagerClient.searchApiDetail(apiId);

        Optional.ofNullable(ibpApiVO)
                .orElseThrow(() -> new ServiceException("非法参数,apiId不存在.",
                        UnionResult.StatusEnum.ParamsFail.code()));

        Optional.of(ibpApiVO)
                .filter(rs -> !rs.getCreateUser().equals(userId))
                .orElseThrow(() -> new ServiceException("不能订阅自己发布的API.",
                        UnionResult.StatusEnum.Fail.code()));

        Optional.of(ibpApiVO)
                .filter(IbpApi::getIsPublic)
                .orElseThrow(() -> new ServiceException("该API还有环境未发布完,不能订阅该API.",
                        UnionResult.StatusEnum.Fail.code()));


        IbpUserConsumerApi consumerApi = apiManagerClient.searchUserConsumerApi(apiId, userId);
        if (Objects.nonNull(consumerApi)
                && Objects.equals(SubscribeApiStatus.APPLYING, consumerApi.getStatus())) {
            throw new ServiceException("当前API正在订阅处理中,请不要重复操作.", UnionResult.StatusEnum.Fail.code());
        }
        if (Objects.nonNull(consumerApi)
                && Objects.equals(SubscribeApiStatus.SUCCESS, consumerApi.getStatus())) {
            throw new ServiceException("当前API已经订阅,请不要重复订阅.", UnionResult.StatusEnum.Fail.code());
        }
        return apiManagerClient.subscribeApi(paramVO);
    }

    @ApiOperation(value = "取消订阅API", response = UnionResult.class)
    @HystrixCommand
    @PostMapping(value = UrlDefine.UNSUBSCRIBE_API, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object unSubscribeApi(
            @ApiParam(name = "paramVO", value = "取消订阅参数实体", required = true)
            @RequestBody @Valid SubscribeApiParamVO paramVO) {

        IbpApiVO ibpApiVO = apiManagerClient.searchApiDetail(Integer.parseInt(paramVO.getApiId()));

        Optional.ofNullable(ibpApiVO)
                .orElseThrow(() -> new ServiceException("非法参数,apiId不存在.",
                        UnionResult.StatusEnum.ParamsFail.code()));

        Optional.of(ibpApiVO)
                .filter(rs -> !rs.getCreateUser().equals(Integer.parseInt(paramVO.getUserId())))
                .orElseThrow(() -> new ServiceException("不能取消自己发布的API.",
                        UnionResult.StatusEnum.Fail.code()));
        return apiManagerClient.unSubscribeApi(paramVO);
    }

    @ApiOperation(value = "删除API", response = UnionResult.class)
    @HystrixCommand
    @DeleteMapping(value = UrlDefine.DELETE_API, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object deleteApi(
            @ApiParam(name = "paramVO", value = "删除参数实体", required = true)
            @RequestBody @Valid DeleteApiParamVO paramVO) {

        IbpApiVO ibpApiVO = apiManagerClient.searchApiDetail(Integer.parseInt(paramVO.getApiId()));
        Optional.ofNullable(ibpApiVO)
                .orElseThrow(() -> new ServiceException("非法参数,apiId不存在.",
                        UnionResult.StatusEnum.ParamsFail.code()));


        Optional.of(ibpApiVO)
                .filter(rs -> !rs.getIsPublic())
                .orElseThrow(() -> new ServiceException("该API已发布,不能删除.",
                        UnionResult.StatusEnum.Fail.code()));
        return apiManagerClient.deleteApi(paramVO);

    }


    @ApiOperation(value = "订阅列表", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.SUBSCRIBE_API_LIST + "/{currentPage}/{pageSize}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object subscribeApiList(@ApiParam(name = "currentPage", value = "当前页数", required = true)
                                   @PathVariable(value = "currentPage") Integer currentPage,
                                   @ApiParam(name = "pageSize", value = "每页显示条数", required = true)
                                   @PathVariable(value = "pageSize") Integer pageSize,
                                   HttpServletRequest request) {
        return apiManagerClient.subscribeApiList(currentPage, pageSize, getUserFromSession(request).getId());
    }

    @ApiOperation(value = "检查是否可以订阅", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.CHECK_SUBSCRIBE_AVAILABLE + "/{apiId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object checkSubscribeIsAvailable(@ApiParam(name = "apiId", value = "apiId", required = true)
                                            @PathVariable(value = "apiId") Integer apiId,
                                            HttpServletRequest request) {
        return apiManagerClient.checkSubscribeIsAvailable(apiId, getUserFromSession(request).getId());
    }

    @ApiOperation(value = "获取发布失败信息", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.DEPLOY_FAIL_MSG + "/{apiId}/{bizType}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchFailMsg(@ApiParam(name = "apiId", value = "apiId", required = true)
                                @PathVariable(value = "apiId") Integer apiId,
                                @ApiParam(name = "bizType",
                                        value = "业务类型[CREATE_API,CREATE_KEY,DEPLOY_KEY,SUBSCRIBE_KEY," +
                                                "UN_SUBSCRIBE_KEY,DELETE_API_KEY]", required = true)
                                @PathVariable(value = "bizType") String bizType,
                                @ApiParam(name = "context", value = "环境/上下文")
                                @RequestParam(value = "context", required = false) String context) {
        return apiManagerClient.searchFailMsg(apiId, bizType, context);
    }


    @ApiOperation(value = "是否公共API", response = UnionResult.class)
    @HystrixCommand
    @PutMapping(value = UrlDefine.PUBLIC_API + "/{apiId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object isPublicAPi(@ApiParam(name = "apiId", value = "apiId", required = true)
                              @PathVariable(value = "apiId") Integer apiId) {

        IbpApiVO ibpApiVO = apiManagerClient.searchApiDetail(apiId);
        Optional.ofNullable(ibpApiVO)
                .orElseThrow(() -> new ServiceException("非法参数,apiId不存在.",
                        UnionResult.StatusEnum.ParamsFail.code()));
        Optional.of(ibpApiVO)
                .filter(IbpApi::getIsPublic)
                .orElseThrow(() -> new ServiceException("该API还有环境未发布完,不能公开该API.",
                        UnionResult.StatusEnum.Fail.code()));
        return apiManagerClient.isPublicAPi(apiId);
    }


    @ApiOperation(value = "根据apiId/用户ID查询订阅API信息", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.SEARCH_SUBSCRIBE_INFO + "/{userId}/{apiId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchUserConsumerApi(@ApiParam(name = "apiId", value = "apiId", required = true)
                                        @PathVariable(value = "apiId") Integer apiId,
                                        @ApiParam(name = "userId", value = "用户登录ID", required = true)
                                        @PathVariable(value = "userId") Integer userId) {
        return apiManagerClient.searchUserConsumerApi(apiId, userId);
    }


    @ApiOperation(value = "根据docsId查询Response列表", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.RESPONSE_CODE_LIST + "/{docsId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchApiDocResponseMgsByDocsId(@PathVariable(value = "docsId") Integer docsId) {
        return apiManagerClient.searchApiDocResponseMgsByDocsId(docsId);
    }

    @ApiOperation(value = "根据responseMsgId查询Response model/headers", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.RESPONSE_MODEL_HEADERS + "/{responseMsgId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchResponseModelAndHeadersById(@PathVariable(value = "responseMsgId") Integer responseMsgId) {
        return apiManagerClient.searchResponseModelAndHeadersById(responseMsgId);
    }

}
